package LockService.Impl;

import LockService.LockService;
import LockService.LockMessage;

import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

public class LockServiceImpl implements LockService {
    private final AtomicInteger clientNumber;
    private final ConcurrentMap<String, LockMessage> lockTable; //当前已分配出去的锁
    private final ConcurrentMap<LockMessage, String> applyTable; //以<clientId, clientApplyId>作为整个系统所有请求的唯一id

    public LockServiceImpl(){
        clientNumber = new AtomicInteger();
        clientNumber.set(0);
        lockTable = new ConcurrentHashMap<>();
        applyTable = new ConcurrentHashMap<>();
    }

    @Override
    public boolean test() {
        System.out.println("RPC test function exe success!");
        return true;
    }

    @Override
    public synchronized boolean acquireLock(LockMessage lockMessage) {// 临界方法，保证多客户端申请锁只有一个能得到锁
        if(applyTable.get(lockMessage)!= null){// 这个if用来实现at-most-once语义，判断对应请求是否已经处理过
            if(applyTable.get(lockMessage).equals("locking")){
                LockMessage oldLock = lockTable.get(lockMessage.getLockedMethod());
                if(new Date().getTime() - oldLock.getLockTime().getTime() < oldLock.getTimeoutSeconds() * 1000L){
                    return true;// 可重入性，如果该申请得到的锁仍然在生效期，则继续返回true
                }
                else {
                    releaseLock(oldLock);
                    return false;// 超时后解锁并返回false
                }
            }
            else {
                return false; // 若之前申请到的锁已释放，直接返回false
            }
        }
        if(!lockTable.containsKey(lockMessage.getLockedMethod())){// 若锁不存在则直接分配给一个申请锁得客户端
            lockMessage.setLockTime(new Date());
            lockMessage.setStatus("locking");
            lockTable.put(lockMessage.getLockedMethod(), lockMessage);
            applyTable.put(lockMessage, lockMessage.getStatus());
            return true;
        }
        else {// 超时控制，简单处理为如果锁已存在，则判断锁是否超时，若超时则解锁分配给其他客户端
            LockMessage oldLock = lockTable.get(lockMessage.getLockedMethod());
            if(new Date().getTime() - oldLock.getLockTime().getTime() < oldLock.getTimeoutSeconds() * 1000L){
                return false;// 未超时则获取锁失败
            }
            else {
                releaseLock(oldLock);// 超时解锁
                lockMessage.setLockTime(new Date());
                lockTable.put(lockMessage.getLockedMethod(), lockMessage);
                applyTable.put(oldLock, oldLock.getStatus());
                System.out.println("Now time is " + new Date().toString());
                System.out.println("The lock of clientId " + oldLock.getClientId() + " is released!");
                return true;
            }
        }
    }

    @Override
    public synchronized boolean releaseLock(LockMessage lockMessage) {
        if(applyTable.get(lockMessage)!= null && applyTable.get(lockMessage).equals("released")){// 已经释放过的锁再释放也该得到true，at-most-once
            return true;
        }
        if(lockTable.containsKey(lockMessage.getLockedMethod())){
            LockMessage oldLock = lockTable.get(lockMessage.getLockedMethod());
            oldLock.setStatus("released");
            applyTable.put(oldLock, oldLock.getStatus());
            applyTable.put(lockMessage, "released");// 记录此次release申请已经成功
            lockTable.remove(lockMessage.getLockedMethod());
            return true;
        }
        else {
            return false;
        }
    }

    @Override
    public int getClientId() {
        return clientNumber.getAndIncrement();
    }
}
